package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author 晚风
 * @since 2023-09-19
 */
@Service
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {


    final CourseClient courseClient;
    final CatalogueClient catalogueClient;
    final LearningRecordMapper recordMapper;

    @Override
    public void addUserLesson(Long userId, List<Long> courseIds) {
        //1. 通过feign远程调用课程服务，查询课程信息
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        //2. 封装po实体类，填充过期时间
        List<LearningLesson> list = new ArrayList<>();
        for (CourseSimpleInfoDTO cinfo : cinfos) {
            LearningLesson lesson = new LearningLesson();
            lesson.setUserId(userId); // 设置用户id
            lesson.setCourseId(cinfo.getId()); // 设置课程id
            Integer validDuration = cinfo.getValidDuration();
            if (validDuration != null){
                LocalDateTime now = LocalDateTime.now();
                lesson.setCreateTime(now);
                lesson.setExpireTime(now.plusMonths(validDuration)); //课程有效期，单位是月
            }
            list.add(lesson);
        }
        //3. 批量保存
        this.saveBatch(list);
    }

    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        // 1. 获取当前登录用户
        Long userId = UserContext.getUser();
        // 2. 分页查询我的课表
        Page<LearningLesson> page = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        // 3. 远程调用课程服务 给vo中的课程名 封面  章节数赋值
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cinfos)){
            throw new BizIllegalException("课程不存在");
        }
        //将cinfos 课程集合 转换为 map结构<课程id,课程对象>
        Map<Long, CourseSimpleInfoDTO> infoDTOMap = cinfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        // 4. 将po中的数据封装到vo
        List<LearningLessonVO> list = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningLessonVO vo = BeanUtils.copyBean(record, LearningLessonVO.class);
            CourseSimpleInfoDTO simpleInfoDTO = infoDTOMap.get(record.getCourseId());
            if (simpleInfoDTO != null){
                vo.setCourseName(simpleInfoDTO.getName()); // 课程名称
                vo.setCourseCoverUrl(simpleInfoDTO.getCoverUrl()); // 课程封面
                vo.setSections(simpleInfoDTO.getSectionNum()); // 课程章节数量
            }
            list.add(vo);
        }
        // 5, 返回结果
        return PageDTO.of(page,list);
    }

    @Override
    public LearningLessonVO queryMyCurrentLesson() {
        // 1. 获取当前登录用户id
        Long userId = UserContext.getUser();
        // 2. 查询当前用户最近学习课程  按latest_learn_time 降序排序  取第一条  正在学习中的  status = 1
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING)
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        // 3. 远程调用课程服务， 给vo中的课程名  封面  章节数赋值
        if (lesson == null){
            return null;
        }
        CourseFullInfoDTO cinfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
        if (cinfo == null){
            throw new BizIllegalException("课程不存在");
        }
        // 4. 查询当前用户课表中 总的课程数
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();
        // 5. 通过哦feign远程调用课程服务  获取小节名称  和小节编号
        Long latestSectionId = lesson.getLatestSectionId();
        List<CataSimpleInfoDTO> cataSimpleInfoDTOS = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(latestSectionId));
        if (CollUtils.isEmpty(cataSimpleInfoDTOS)){
            throw new BizIllegalException("小节不存在");
        }
        // 6. 封装到vo返回
        LearningLessonVO lessonVO = BeanUtils.copyBean(lesson, LearningLessonVO.class);
        lessonVO.setCourseName(cinfo.getName());
        lessonVO.setCourseCoverUrl(cinfo.getCoverUrl());
        lessonVO.setSections(cinfo.getSectionNum());
        lessonVO.setCourseAmount(count); // 当前用户能学习课程总数
        CataSimpleInfoDTO simpleInfoDTO = cataSimpleInfoDTOS.get(0);
        lessonVO.setLatestSectionName(simpleInfoDTO.getName()); // 最近学习的小节名称
        lessonVO.setLatestSectionIndex(simpleInfoDTO.getCIndex()); // 最近学习的小节序号
        return lessonVO;
    }

    @Override
    public Long isLessonValid(Long courseId) {
        // 1. 获取用户id
        Long userId = UserContext.getUser();
        // 2. 根据用户id和课程id查询课程表
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getUserId, userId).eq(LearningLesson::getCourseId, courseId).one();
        if (lesson == null){
            return null;
        }
        // 3. 判断课程是否过期
        LocalDateTime expireTime = lesson.getExpireTime();
        LocalDateTime now = LocalDateTime.now();
        if (expireTime != null && now.isAfter(expireTime)){
            return null;
        }
        return lesson.getId();
    }

    @Override
    public LearningLessonVO queryLessonByCourseId(Long courseId) {
        // 1. 获取用户id
        Long userId = UserContext.getUser();
        // 2. 根据用户id和课程id查询课程表
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getUserId, userId).eq(LearningLesson::getCourseId, courseId).one();
        if (lesson == null){
            return null;
        }
        // 3. 对象拷贝
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    @Override
    public void createLearningPlans(LearningPlanDTO dto) {
        // 1. 获取用户id
        Long userId = UserContext.getUser();
        // 2. 根据用户id和课程id查询课程表
        LearningLesson lesson = this.lambdaQuery().eq(LearningLesson::getUserId, userId).eq(LearningLesson::getCourseId, dto.getCourseId()).one();
        if (lesson == null){
            throw new BizIllegalException("该课程没有加入课表");
        }
        this.lambdaUpdate().set(LearningLesson::getWeekFreq,dto.getFreq())
                .eq(LearningLesson::getId,lesson.getId())
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .update();
    }

    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        // 1. 获取用户id
        Long userId = UserContext.getUser();
        //todo 2. 查询积分
        QueryWrapper<LearningLesson> wrapper = new QueryWrapper<>();
        // 3. 查询本周学习计划总数
        wrapper.select("sum(week_freq) as plansTotal");
        wrapper.eq("user_id",userId);
        wrapper.in("status",LessonStatus.LEARNING,LessonStatus.NOT_BEGIN);
        wrapper.eq("plan_status", PlanStatus.PLAN_RUNNING);
        Map<String, Object> map = this.getMap(wrapper);
        Integer plansTotal = 0;
        if (map != null && map.get("plansTotal")!= null){
             plansTotal = Integer.valueOf(map.get("plansTotal").toString());

        }
        // 4. 查询本周已学习计划总数
        LocalDate now = LocalDate.now();
        LocalDateTime weekBeginTime = DateUtils.getWeekBeginTime(now);
        LocalDateTime weekEndTime = DateUtils.getWeekEndTime(now);
        Integer weekFinishedPlanNum = recordMapper.selectCount(Wrappers.<LearningRecord>lambdaQuery()
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                .between(LearningRecord::getFinishTime, weekBeginTime, weekEndTime));

        // 5. 查询课表数据 learning_lessons 条件 userId status in (0,1) plan_status = 1 分页
        Page<LearningLesson> lessonPage = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = lessonPage.getRecords();
        if (CollUtils.isEmpty(records)){
            LearningPlanPageVO vo = new LearningPlanPageVO();
            vo.setPages(0L);
            vo.setTotal(0L);
            vo.setList(new ArrayList<>());
            return vo;
        }
        // 6. 远程调用课程服务，获取课程信息
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        List<CourseSimpleInfoDTO> cinfos = courseClient.getSimpleInfoList(courseIds);
        if (CollUtils.isEmpty(cinfos)){
            throw new BizIllegalException("课程不存在");
        }
//        将cinfos list结构转换为map结构
        Map<Long, CourseSimpleInfoDTO> cInfoMap = cinfos.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        // 7. 查询课程记录表  本周当前用户下，每一门课下已学习的小节数量
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("lesson_id as lessonId ","count(*) as userId");
        queryWrapper.eq("user_id",userId);
        queryWrapper.eq("finished",true);
        queryWrapper.between("finish_time",weekBeginTime,weekEndTime);
        queryWrapper.groupBy("lesson_id");
        List<LearningRecord> learningRecords = recordMapper.selectList(queryWrapper);
        // map中的key是lessonId value是当前用户下对该课程以学习的课程小节数
        Map<Long, Long> courseWeekFinishMap = learningRecords.stream().collect(Collectors.toMap(LearningRecord::getLessonId, c -> c.getUserId()));
        // 8. 封装vo返回
        LearningPlanPageVO vo = new LearningPlanPageVO();
        vo.setWeekTotalPlan(plansTotal);
        vo.setWeekFinished(weekFinishedPlanNum);

        List<LearningPlanVO> weekList = new ArrayList<>();
        for (LearningLesson record : records) {
            LearningPlanVO planVO = BeanUtils.copyBean(record, LearningPlanVO.class);
            CourseSimpleInfoDTO infoDTO = cInfoMap.get(record.getCourseId());
            if (infoDTO != null){
                planVO.setCourseName(infoDTO.getName()); // 课程名称
                planVO.setSections(infoDTO.getSectionNum()); // 课程下的总小节数
            }
            planVO.setWeekLearnedSections(courseWeekFinishMap.getOrDefault(record.getId(),0L).intValue()); // 本周已学习章节数
            weekList.add(planVO);
        }
//        vo.setList(weekList);
//        vo.setTotal(lessonPage.getTotal());
//        vo.setPages(lessonPage.getPages());
//        return vo;
        return vo.pageInfo(lessonPage.getTotal(),lessonPage.getPages(),weekList);
    }


}
